/*
 * $Id: IPTDSThreadPoolManager.java,v 1.5 2009/07/10 19:51:30 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.processor.iptds.worker;

import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.springframework.util.Assert;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.processor.iptds.IPTDSProperties;

/**
 * 
 * @author 212042946
 * 
 */
public class IPTDSThreadPoolManager {

	private ThreadPoolExecutor executor;
	private IPTDSProperties iptdsProperties = null;
	private int urgentWorkers = 1;
	private Vector<IPTDSWorker> activeWorkers = new Vector<IPTDSWorker>();

	public IPTDSThreadPoolManager() {
	}

	public IPTDSThreadPoolManager(IPTDSProperties iptdsProperties) {
		this.iptdsProperties = iptdsProperties;
		this.urgentWorkers = iptdsProperties.getUrgentWorkers();
		init();
	}

	public void init() {
		Assert
				.notNull(iptdsProperties,
						"IPTDSProperties is not defined. Use setIPTDSProperties to provide one");
		/*
		 * Fixed size thread pool with unbounded queue. The class has no intent
		 * to use the queue because we should not queue up IPTDSClient with
		 * socket resource attached to it, or the socket could time out when the
		 * IPTDSClient is actually executed.
		 */
		executor = new ThreadPoolExecutor(iptdsProperties.getIPTDSWorkerNum(),
				iptdsProperties.getIPTDSWorkerNum(), Long.MAX_VALUE,
				TimeUnit.NANOSECONDS, new LinkedBlockingQueue<Runnable>());
	}

	public synchronized void execute(IPTDSWorker worker) {
		activeWorkers.add(worker);
		executor.execute(worker);
	}

	/**
	 * Remove IPTDSClient from the running IPTDSClient list. This is a callback
	 * method from IPTDSClient.
	 * 
	 * @param worker
	 */
	public synchronized void removeWorker(IPTDSWorker worker) {
		activeWorkers.remove(worker);
	}

	/**
	 * Initiate stop to all running IPTDSClient. Then shutdown the thread pool.
	 */
	public void stop() {
		ASCLogger
				.info(this.getClass(), "stop", "Stopping threadpoolManager...");
		synchronized (this) {
			Vector<IPTDSWorker> workerList = new Vector<IPTDSWorker>(
					activeWorkers);
			Iterator<IPTDSWorker> iter = workerList.iterator();
			while (iter.hasNext()) {
				IPTDSWorker worker = (IPTDSWorker) iter.next();
				worker.stop();
			}
			workerList.clear();
		}
		executor.shutdown();
		try {
			executor.awaitTermination(iptdsProperties
					.getShutdownGracePeriodInSec(), TimeUnit.SECONDS);
		} catch (InterruptedException ie) {
			ASCLogger.warn(this.getClass(), "stop",
					"Interrupted while waiting workers gracefully terminate themselves.\n"
							+ "Perform force shutdown.", ie);
		}
		executor.shutdownNow();
	}

	public boolean isStop() {
		return executor.isShutdown();
	}

	/**
	 * Determine if there is any more available worker.
	 * 
	 * @return true/false
	 */
	public boolean anyWorker() {
		/*
		 * Since the active count is an approximate value, also need to check
		 * the queue to make sure nothing is being queued up.
		 */
		if ((executor.getActiveCount() >= executor.getMaximumPoolSize())
				|| (executor.getQueue().size() > 0)) {
			return false;
		}
		return true;
	}

	/**
	 * Get the reserved number of urgent workers
	 * 
	 * @return number of urgent workers
	 */
	public int getUrgentWorkersNum() {
		return this.urgentWorkers;
	}

	public int getAvailableWorker() {
		return (executor.getMaximumPoolSize() - executor.getActiveCount());
	}

	public int getMaximumWorker() {
		return executor.getMaximumPoolSize();
	}

	public void setIPTDSProperties(IPTDSProperties iptdsProperties) {
		this.iptdsProperties = iptdsProperties;
	}
}
